package com.zeyu.framework;

import com.zeyu.framework.core.configuration.QuartzConfiguration;
import com.zeyu.framework.core.web.listener.InstantiationTracingBeanPostProcessor;
import com.zeyu.framework.core.web.listener.WebContextListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.util.WebAppRootListener;

import javax.servlet.ServletContextListener;
import javax.servlet.SessionTrackingMode;
import java.util.Collections;

/**
 * 框架的启动入口,可以分布式部署,具体注意事项请参照readme.md
 * 启动过程中增加事件监听机制
 * 1. ApplicationStartedEvent ---> spring boot启动开始时执行的事件
 * 2. ApplicationEnvironmentPreparedEvent  --> spring boot 对应Enviroment已经准备完毕，但此时上下文context还没有创建
 * 3. ApplicationPreparedEvent ---> spring boot上下文context创建完成，但此时spring中的bean是没有完全加载完成的
 * 4. ApplicationFailedEvent ---> spring boot启动异常时执行事件
 * 实现监听步骤：
 * 1. 监听类实现ApplicationListener接口
 * 2. 将监听类添加到SpringApplication实例
 * Created by zeyuphoenix on 16/7/19.
 */
@SpringBootApplication
@ServletComponentScan   // 注册扫描我们自己写的servlet
// 继承SpringBootServletInitializer只是为我们打war包时候使用,为了开发方便使用jar方式,打包war,需要修改pom.xml
public class FrameworkApplication extends SpringBootServletInitializer {

    // ================================================================
    // Constants
    // ================================================================

    /**
     * logger
     */
    private static final Logger logger = LoggerFactory.getLogger(FrameworkApplication.class);

    /**
     * 标识系统是以 jar 启动还是以 tomcat server 方式启动
     * 主要是用于处理 jar 包内包含 dll 和 jar 的情况
     */
    public static volatile boolean TOMCAT_SERVER_STARTING = false;

    // ================================================================
    // Fields
    // ================================================================

    // ================================================================
    // Constructors
    // ================================================================

    // ================================================================
    // Methods from/for super Interfaces or SuperClass
    // ================================================================

    /**
     * 用与在web.xml中配置负责初始化Spring应用上下文的监听器作用类似，只不过不需要编写额外的XML文件
     */
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {

        // tomcat 外部启动时,需要把main启动的listener也加载一遍
        application.listeners(
                // 加载自动扫描的quartz listener
                new QuartzConfiguration.AutoConfigurationQuartzListener(),
                // 打印版本信息,检查数据库启动
                new WebContextListener(),
                // bean都初始化完成后的操作
                new InstantiationTracingBeanPostProcessor());

        // 以 tomcat  server 启动
        TOMCAT_SERVER_STARTING = true;
        logger.info("boot with tomcat server");

        return application.sources(FrameworkApplication.class);
    }

    // ================================================================
    // Public or Protected Methods
    // ================================================================

    @Bean
    public ServletContextInitializer servletContextInitializer() {
        return servletContext -> {
            // 移除<tracking-mode>URL</tracking-mode> 解决jsessionid的问题
            servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE));
            // 初始化web配置参数
            servletContext.setInitParameter("webAppRootKey", "framework.root");
        };
    }

    /**
     * 利用Spring随时随地获得Request和Session
     */
    @Bean
    @ConditionalOnMissingBean(RequestContextListener.class)
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }

    /**
     * 通过System.getProperty("framework.root")获取了web根目录
     */
    @Bean
    public ServletContextListener webAppRootListener() {
        return new WebAppRootListener();
    }


    // ================================================================
    // Getter & Setter
    // ================================================================

    // ================================================================
    // Private Methods
    // ================================================================

    // ================================================================
    // Inner or Anonymous Class
    // ================================================================

    // ================================================================
    // Test Methods
    // ================================================================

    /**
     * bootstrap the application.
     */
    public static void main(String[] args) {
        // start in here
        SpringApplication springApplication = new SpringApplication(FrameworkApplication.class);

        // 加载自动扫描的quartz listener
        springApplication.addListeners(new QuartzConfiguration.AutoConfigurationQuartzListener());

        // 打印版本信息,检查数据库启动
        springApplication.addListeners(new WebContextListener());
        // bean都初始化完成后的操作
        springApplication.addListeners(new InstantiationTracingBeanPostProcessor());

        springApplication.run(args);

        logger.info("framework is starting...");
    }

}
